package terra.managers;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


public class ThreadPoolManager
{
	public ScheduledThreadPoolExecutor _generalScheduledThreadPool;
	public ThreadPoolExecutor _generalThreadPool;
	public ScheduledThreadPoolExecutor _saveThreadPool;
	private ThreadPoolExecutor _loadThreadPool;
	private ThreadPoolExecutor _createThreadPool;
	public ThreadPoolExecutor _IOThreadPool;	
	private static final long MAX_DELAY = Long.MAX_VALUE / 1000000 / 2;
	private boolean _shutdown;

	public static ThreadPoolManager getInstance()
	{
		return SingletonHolder._instance;
	}

	private ThreadPoolManager()
	{
		_generalScheduledThreadPool = new ScheduledThreadPoolExecutor(10, new PriorityThreadFactory("General ThreadPool", Thread.NORM_PRIORITY));
		_generalThreadPool = new ThreadPoolExecutor(3, 3 + 1, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("General ThreadPool", Thread.NORM_PRIORITY));
		_saveThreadPool  = new ScheduledThreadPoolExecutor(2, new PriorityThreadFactory("Save ThreadPool", Thread.MIN_PRIORITY));
		_createThreadPool  = new ThreadPoolExecutor(1, 2 + 1, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("Create ThreadPool", Thread.NORM_PRIORITY-1));
		_loadThreadPool  = new ThreadPoolExecutor(1, 1 + 1, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("Load ThreadPool", Thread.NORM_PRIORITY-3));
		_IOThreadPool = new ThreadPoolExecutor(4, 8 + 1, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("IO ThreadPool", Thread.NORM_PRIORITY+2));
	}

	public static long validateDelay(long delay)
	{
		if (delay < 0)
			delay = 0;
		else if (delay > MAX_DELAY)
			delay = MAX_DELAY;
		return delay;
	}

	public ScheduledFuture<?> scheduleGeneral(Runnable r, long delay)
	{
		try
		{
			delay = ThreadPoolManager.validateDelay(delay);
			return _generalScheduledThreadPool.schedule(r, delay, TimeUnit.MILLISECONDS);
		}
		catch (RejectedExecutionException e)
		{
			return null; /* shutdown, ignore */
		}
	}

	public ScheduledFuture<?> scheduleGeneralAtFixedRate(Runnable r, long initial, long delay)
	{
		try
		{
			delay = ThreadPoolManager.validateDelay(delay);
			initial = ThreadPoolManager.validateDelay(initial);
			return _generalScheduledThreadPool.scheduleAtFixedRate(r, initial, delay, TimeUnit.MILLISECONDS);
		}
		catch (RejectedExecutionException e)
		{
			return null; /* shutdown, ignore */
		}
	}

	public ScheduledFuture<?> scheduleSave(Runnable r, long delay)
	{
		try
		{
			delay = ThreadPoolManager.validateDelay(delay);
			return _saveThreadPool.schedule(r, delay, TimeUnit.MILLISECONDS);
		}
		catch (RejectedExecutionException e)
		{
			return null; /* shutdown, ignore */
		}
	}
	
	public void removeRunnable(Runnable r)
	{
		if (r == null)
			return;
		_generalThreadPool.remove(r);
		_generalScheduledThreadPool.remove(r);
		_saveThreadPool.remove(r);
		_loadThreadPool.remove(r);
		_createThreadPool.remove(r);
		purge();
	}
	
	public void executeIO(Runnable r) {
		_IOThreadPool.execute(r);
	}
	
	public void executeTask(Runnable r)	{
		_generalThreadPool.execute(r);
	}

	public void executeSave(Runnable r)
	{
		_saveThreadPool.execute(r);
	}

	public void executeLoad(Runnable r)
	{
		_loadThreadPool.execute(r);
	}

	public void executeCreate(Runnable r)
	{
		_createThreadPool.execute(r);
	}	
	
	private class PriorityThreadFactory implements ThreadFactory
	{
		private int _prio;
		private String _name;
		private AtomicInteger _threadNumber = new AtomicInteger(1);
		private ThreadGroup _group;

		public PriorityThreadFactory(String name, int prio)
		{
			_prio = prio;
			_name = name;
			_group = new ThreadGroup(_name);
		}

		public Thread newThread(Runnable r)
		{
			Thread t = new Thread(_group, r);
			t.setName(_name + "-" + _threadNumber.getAndIncrement());
			t.setPriority(_prio);
			return t;
		}
	}

	public void shutdown()
	{
		_shutdown = true;
		try
		{
			_generalScheduledThreadPool.awaitTermination(1, TimeUnit.SECONDS);
			_generalThreadPool.awaitTermination(1, TimeUnit.SECONDS);
			_saveThreadPool.awaitTermination(1, TimeUnit.SECONDS);
			_generalScheduledThreadPool.shutdown();
			_generalThreadPool.shutdown();
			_saveThreadPool.shutdown();
		}
		catch (InterruptedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public boolean isShutdown()
	{
		return _shutdown;
	}

	public void purge()
	{
		_generalScheduledThreadPool.purge();
		_generalThreadPool.purge();
		_saveThreadPool.purge();
	}


	@SuppressWarnings("synthetic-access")
	private static class SingletonHolder
	{
		protected static final ThreadPoolManager _instance = new ThreadPoolManager();
	}
}